/*
 * @Description:
 * @Author: wangqi
 * @Date: 2021-07-03 13:37:09
 * @LastEditTime: 2021-07-07 14:21:29
 */

/**
 *  Promise
    .then
    .catch
    .finally
    .all    =>  Promise.all([promise1, promise2]);  所有promsie执行成功该实例才返回fulfilled， 否则返回rejected;
    .allSettled => Promise.allSettled([promise1, promise2]); 数组内所有的promsie都执行完，不管是成功还是失败，该实例的值就是所有promsie的参数结果;
    .race   =>  Promise.race([promise1, promise2]); 看哪个promsie先执行完成，then的值就是该返回值;
    .any    => Promise.any([promsie1, promise2]);   数组内只要存在一个成功，该实例就是fulfilled；全部rejected,实例才变成rejected;

    .resolve
    .reject

    特点：
        1. 只有三种状态且不受外界影响; pending, fulfilled, rejected
        2. 一旦状态改变，就不会再变;
            resolve("ok"); throw new Error("fail"); 

    
        参考：
            https://mp.weixin.qq.com/s/HNa7JGtxG4k2Oh28ARF3fw
            https://mp.weixin.qq.com/s/3JlGG9VMo4ySNQwyYx1BxA
 */


const pending = 'pending';
const fulfilled = 'fulfilled';
const rejected = 'rejected';
const isFunction = (value) => typeof value === 'function';

class WqPromise {
    constructor(executor) {
        if (isFunction(executor)) throw new Error('executor必须是一个函数');
        this._value = '';
        this._status = pending;
        this.fulfilledQueue = [];
        this.rejectedQueue = [];

        try {
            executor(this._resolve.bind(this), this._reject.bind(this));
        } catch (error) {
            this._reject(error);
        }

    }

    _resolve(data) {
        if (this._status !== pending) return;
        let run = () => {
            this._status = fulfilled;
            let runResolved = (value) => {
                let cb;
                while (cb = this.fulfilledQueue.shift()) {
                    cb(value);
                }
            };
            let runRejected = (error) => {
                let cb;
                while (cb = this.rejectedQueue.shift()) {
                    cb(error);
                }
            };

            if (data instanceof WqPromise) {
                data.then((value) => {
                    this._value = value;
                    runResolved(value);
                }, (error) => {
                    this._value = error;
                    runRejected(error);
                });
            } else {
                this._value = data;
                runRejected(data);
            }
        };
        setTimeout(run, 0);
    }

    _reject(error) {
        if (this._status !== pending) return;
        let run = () => {
            let cb;
            this._status = rejected;
            this._value = error;
            // 循环执行队列中的回调
            while (cb = this.rejectedQueue.shift()) {
                cb(error);
            }
        };
        setTimeout(run, 0);
    }

    then(onResolve, onReject) {
        let { _value, _status } = this;
        return new WqPromise((onResolveNext, onRejectNext) => {

            let resolveCallBack = (value) => {
                try {
                    // 一般不存在不是函数情况
                    if (!isFunction(onResolve)) {
                        onResolveNext(value);
                    }
                    let res = onResolve(value);
                    if (res instanceof WqPromise) {
                        res.then(onResolveNext, onRejectNext);
                    } else {
                        onResolveNext(value);
                    }
                } catch (error) {
                    onRejectNext(error);
                }
            };

            let rejectedCallBack = (error) => {
                try {
                    if (!isFunction(onReject)) {
                        onRejectNext(error);
                    } else {
                        let res = onReject(value);
                        if (res instanceof WqPromise) {
                            res.then(onResolveNext, onRejectNext);
                        } else {
                            onRejectNext(error);
                        }
                    }
                } catch (error) {
                    onRejectNext(error);
                }
            };

            switch (_status) {
                case 'pending':
                    this.fulfilledQueue.push(resolveCallBack);
                    this.rejectedQueue.push(rejectedCallBack);
                    break;

                case 'fulfilled':
                    onResolve(_value);
                    break;
                case 'rejected':
                    onReject(_value);
                    break;

                default:
                    break;
            }
        });
    }

    catch(onReject) {
        return this.then(undefined, onReject);
    }

    fillnay(callBack) {
        return new WqPromise(
            value => Promise.resolve(callBack()).then(() => value),
            error => Promise.reject(callBack()).then(() => error),
        )
    }

    all(arr) {
        return new WqPromise((resolve, reject) => {
            let values = [];
            let count = 0;
            for (let [index, item] of arr.entries()) {
                this.resolve(item).then((data) => {
                    count++;
                    values.push(data);
                    if (count == arr.length) {
                        resolve(arr);
                    }
                }, (error) => {
                    resolve(error)
                });
            }
        })
    }

    race(arr) {
        return new WqPromise((resolve, reject) => {
            for (let [index, item] of arr.entries()) {
                this.resolve((data) => {
                    resolve(data);
                }, (error) => {
                    reject(error);
                })
            }
        });
    }

    any() { }

    static reolve(value) {
        if (value instanceof WqPromise) return value;
        return new WqPromise((resolve, reject) => resolve(value));
    }

    static reject(error) {
        return new WqPromise((resolve, reject) => reject(error));
    }
}




